package com.cupdata.zicon.jdk7concurrent.chapter3;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * When you create a CountDownLatch object, the object uses the constructor's parameter
to initialize an internal counter. Every time a thread calls the countDown() method, the
CountDownLatch object decrements the internal counter in one unit. When the internal
counter arrives to 0, the CountDownLatch object wakes up all the threads that were waiting
in the await() method.

There are some differences with respect to other synchronization methods, which are
as follows:
(1) The CountDownLatch mechanism is not used to protect a shared resource or a
critical section. It is used to synchronize one or more threads with the execution of
various tasks.
(2) It only admits one use. As we explained earlier, once the counter of
CountDownLatch arrives at 0, all the calls to its methods have no effect.
You have to create a new object if you want to do the same synchronization again.

 * @author SunYabing
 *
 */
public class CountDownLatchTest {
	public static void main(String[] args) {
		Videoconference conference = new Videoconference(10);
		Thread threadConference = new Thread(conference);
		threadConference.start();

		for (int i = 0; i < 10; i++) {
			Participant p = new Participant(conference, "Participant " + i);
			Thread t = new Thread(p);
			t.start();
		}
	}
}

class Videoconference implements Runnable {
	private final CountDownLatch controller;

	public Videoconference(int number) {
		controller = new CountDownLatch(number);
	}

	public void arrive(String name) {
		System.out.printf("%s has arrived.", name);
		controller.countDown();
		System.out.printf("VideoConference: Waiting for %d participants.\n",
				controller.getCount());
	}

	@Override
	public void run() {
		System.out.printf(
				"VideoConference: Initialization: %d participants.\n",
				controller.getCount());
		try {
			controller.await();
			System.out
					.printf("VideoConference: All the participants have come\n");
			System.out.printf("VideoConference: Let's start...\n");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}
}

class Participant implements Runnable {
	private Videoconference conference;
	private String name;

	public Participant(Videoconference conference, String name) {
		this.conference = conference;
		this.name = name;
	}

	@Override
	public void run() {
		long duration = (long) (Math.random() * 10);
		try {
			TimeUnit.SECONDS.sleep(duration);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		conference.arrive(name);
	}
}
